/*
ESC命令集（普通打印机,蓝牙热敏打印机）

命令方式:
1.可以用字符串拼接 ""+(char)10
2.可以用char[] cmd_chars=new char[1];
cmd_chars[0]=10;
3.可以用byte[] cmd_bytes=new byte[1];
cmd_bytes[0]=10;

推荐品牌:xprinter

 */

import 'dart:typed_data';

import 'package:xsdk_flutter/xsdk/utils/print/base/x_utils_print_command_base.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_log.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_string.dart';

class XBaseESCPrintCommandUtils {

  static const String PrintCommand_ESC_Initialize_Printer = "PrintCommand_ESC_Initialize_Printer";
  static const String PrintCommand_ESC_Text_underline = "PrintCommand_ESC_Text_underline";
  static const String PrintCommand_ESC_Text_Size = "PrintCommand_ESC_Text_Size";
  static const String PrintCommand_ESC_Text_Align_justification = "PrintCommand_ESC_Text_Align_justification";
  static const String PrintCommand_ESC_Text_Background_black = "PrintCommand_ESC_Text_Background_black";
  static const String PrintCommand_ESC_NewLine = "PrintCommand_ESC_NewLine";//输出 或 单行推纸
  static const String PrintCommand_ESC_NewLines = "PrintCommand_ESC_NewLines";//多行推纸(当多个PrintCommand_ESC_NewLine无效时使用)
  static const String PrintCommand_ESC_openCash = "PrintCommand_ESC_openCash";
  static const String PrintCommand_ESC_PageLine = "PrintCommand_ESC_PageLine";
  static const String PrintCommand_ESC_CutPaper = "PrintCommand_ESC_CutPaper";
  static const String PrintCommand_ESC_Text_bold = "PrintCommand_ESC_Text_bold";


  //注意: 文字+图片封装成一条打印命令时, 文字部分要转为byte[],再拼接图片部分的byte[]即可

  //打印条码(一维码)///////////////////////////////////////////////////////////////////
  /*
    条码(一维码),ok
    参数:
    String barcode: 条码内容
    注意:
    1.打印条码时,打印机缓存必须为空,即已打印完之前的内容,否则打印机忽略,因此打印条码将会是自己独立一行的
    2.使用code128生成条码,需要在数据前添加字符集code b(不要选择code c,数据会错乱)

     */
  static String esc_barcode(
      String barcode,
      bool isShowBarcode,
      bool isDefaultHeight,
      int align
      ){
    if (XStringUtils.isEmpty(barcode)){
      return "";
    }

    //保证缓存清空,否则会被打印机忽略
    String initCommand=getPrintCommand_ESC(PrintCommand_ESC_Initialize_Printer,null);

    String alignCommand="";
    if (align==1){
      alignCommand=getPrintCommand_ESC(PrintCommand_ESC_Text_Align_justification,[1]);
    }else if (align==2){
      alignCommand=getPrintCommand_ESC(PrintCommand_ESC_Text_Align_justification,[2]);
    }else{
      alignCommand=getPrintCommand_ESC(PrintCommand_ESC_Text_Align_justification,[0]);
    }

    //是否显示条码内容
    String showBarcodeCommand="";
    if (isShowBarcode){
      //显示在条码下方
      showBarcodeCommand="${String.fromCharCode(29)}${String.fromCharCode(27)}${String.fromCharCode(2)}";
      //字体大小默认0 12*24  (1为9*17)
      showBarcodeCommand+="${String.fromCharCode(29)}${String.fromCharCode(102)}${String.fromCharCode(0)}";
    }else{
      showBarcodeCommand="${String.fromCharCode(29)}${String.fromCharCode(72)}${String.fromCharCode(0)}";
    }

    //每条线的基准宽度,这里固定为2,实际总宽度会根据内容多少决定
    String barcodeEachWidthCommand="${String.fromCharCode(29)}${String.fromCharCode(119)}${String.fromCharCode(2)}";

    //条码的总高度:value[0]=50/80/100 (范围:1 ≤ n ≤ 255)
    int height=50;
    if (!isDefaultHeight){
      //大
      height=80;
    }
    String barcodeHeightCommand="${String.fromCharCode(29)}${String.fromCharCode(104)}${String.fromCharCode(height)}";

    //使用code128生成条码时,需要添加指定字符code b: (char)123+(char)66,
    //因此长度需要增加2,否则条码数据不完整,(生成条码会自动忽略指定字符命令)
    String codeB="${String.fromCharCode(123)}${String.fromCharCode(66)}";
    int length=XBasePrintCommandUtils.getBytesLength_GBK(barcode)+2;

    //条码内容
    String barcodeCommand="${String.fromCharCode(29)}${String.fromCharCode(107)}${String.fromCharCode(73)}${String.fromCharCode(length)}$codeB$barcode";

    //注意:命令末尾不需要添加换行命令
    return initCommand+alignCommand+showBarcodeCommand+barcodeEachWidthCommand+barcodeHeightCommand+barcodeCommand;
  }


  /*
    直出二维码ok
     */
  static String esc_qrcode(String qrcode,bool isDefaultWidth,int align){

    //设置二维码大小
    int qrWidth = 9;//默认9 正常大小(最大)

    if (!isDefaultWidth){
      //小
      qrWidth = 5;
    }

    String alignCommand="";

    if (align==1){
      alignCommand=getPrintCommand_ESC(PrintCommand_ESC_Text_Align_justification,[1]);
    }else if (align==2){
      alignCommand=getPrintCommand_ESC(PrintCommand_ESC_Text_Align_justification,[2]);
    }

    int length = XBasePrintCommandUtils.getBytesLength_GBK(qrcode);

    String qrC1="${String.fromCharCode(29)}${String.fromCharCode(40)}${String.fromCharCode(107)}${String.fromCharCode(3)}${String.fromCharCode(0)}${String.fromCharCode(49)}${String.fromCharCode(67)}${String.fromCharCode(qrWidth)}";

    //设置二维码纠错等级
    String qrC2="${String.fromCharCode(29)}${String.fromCharCode(40)}${String.fromCharCode(107)}${String.fromCharCode(3)}${String.fromCharCode(0)}${String.fromCharCode(49)}${String.fromCharCode(69)}${String.fromCharCode(49)}";
    //二维码内容
    String qrC3="${String.fromCharCode(29)}${String.fromCharCode(40)}${String.fromCharCode(107)}${String.fromCharCode(3+length)}${String.fromCharCode(0)}${String.fromCharCode(49)}${String.fromCharCode(80)}${String.fromCharCode(48)}$qrcode";

    //存储到打印机中
    String qrC4="${String.fromCharCode(29)}${String.fromCharCode(40)}${String.fromCharCode(107)}${String.fromCharCode(3)}${String.fromCharCode(0)}${String.fromCharCode(49)}${String.fromCharCode(81)}${String.fromCharCode(48)}"
    ;

    //注意:不用在末尾添加换行,已可以直接输出打印
    return alignCommand+qrC1+qrC2+qrC3+qrC4;

  }


  /*
    获得常用打印机字体式样命令(详细见爱普生打印pdf文档)
    注意:
    在拼接命令和内容过程中,中途在没有添加换行命令打印输出前,不要添加初始化命令,会导致没输出的内容缓存清空
     */
  static String getPrintCommand_ESC(String type, List<int>? values) {

    String command="";

    try{

      //通用打印命令 ESC/POS控制指令
      switch (type){
        case PrintCommand_ESC_Initialize_Printer:
        //初始化打印机,会清除其他设备和清空打印内容缓存
          command="${String.fromCharCode(27)}${String.fromCharCode(64)}";
          break;
        case PrintCommand_ESC_NewLine:
        //如果缓存区有数据则打印输出并新起一行,如果缓存区没有内容则直接换行;
        //注意:打印内容最后要拼接这个换行输出,否则缓存区内容不够一行不会输出的
          command=String.fromCharCode(10);//\n
          break;
        case PrintCommand_ESC_NewLines:
        //多行推纸(当有些打印机使用多个\n(PrintCommand_ESC_NewLine)失效时替代使用,如SPDA内置打印机)
        //相当于 Uint8List.fromList([27,100,n]);
          if (values!=null && values.isNotEmpty){
            int value=values[0];
            command="${String.fromCharCode(27)}${String.fromCharCode(100)}${String.fromCharCode(value)}";
          }else{
            //默认1行
            command="${String.fromCharCode(27)}${String.fromCharCode(100)}${String.fromCharCode(1)}";
          }
          break;
        case PrintCommand_ESC_openCash:
        /*
                打开钱箱
                [命令]
                ASCII 码 ESC p m t1 t2
                十六进制码 1B 70 m t1 t2
                十进制码 27 112 m t1 t2
                [范围]m = 0, 1, 48, 49 0 ≤ t1 ≤ 255, 0 ≤ t2 ≤ 255
                [描述]输出由 t1 和 t2 设定的钱箱开启脉冲到由 m 指定的引脚： M 连接引脚 0, 48 钱箱 插座的引脚 2 1, 49 钱箱插座的引脚 5
                [注释] • 钱箱开启脉冲高电平时间为[t1 × 2 ms]，低点平时间为[t2 × 2 ms]。 • 如果 t2 < t1，低电平时间为[t1 × 2 ms]。

                注意:不能加换行输出命令(char)10结尾,会导致打印机每次都出一行纸;
                 */
          command="${String.fromCharCode(27)}${String.fromCharCode(112)}${String.fromCharCode(48)}${String.fromCharCode(49)}${String.fromCharCode(49)}";//打开钱箱
          break;
        case PrintCommand_ESC_Text_Size://字体大小ok
        /*
                命令:
                ASCII 码 GS ! n
                十六进制码 1D 21 n
                十进制码 29 33 n
                [范围] 0 ≤ n ≤ 255 （1 ≤ 纵向放大倍数 ≤ 8，1 ≤ 横向放大倍数 ≤ 8）
                [描述] 一个 byte 有 8 位，用 0 到 2 位选择字符高度，4 到 6 位选择字符宽度
                00：默认
                17：放大1倍高宽 二进制:0 001 0 001 = 十进制17
                34：放大2倍高宽 二进制:0 010 0 010 = 十进制34
                 */
        //command=""+(char)29+(char)33+(char)17; 常用放大1倍

          if (values!=null && values.isNotEmpty){
            int value=values[0];
            if (value==1){
              //放大1倍
              command="${String.fromCharCode(29)}${String.fromCharCode(33)}${String.fromCharCode(17)}";
            }else if (value==2){
              //放大2倍
              command="${String.fromCharCode(29)}${String.fromCharCode(33)}${String.fromCharCode(34)}";
            }else{
              //默认大小
              command="${String.fromCharCode(29)}${String.fromCharCode(33)}${String.fromCharCode(0)}";
            }

          }else{
            //默认大小
            command="${String.fromCharCode(29)}${String.fromCharCode(33)}${String.fromCharCode(0)}";
          }

          break;
        case PrintCommand_ESC_Text_bold:
        //字体加粗命令
        //注意:加粗字体拼接命令后需要输出(后加换行符),否则不生效(不输出会被后面的初始化命令替代),因此加粗后只能换行输出了
        //使用: 加粗命令+需要加粗的字体+取消加粗命令+ 换行输出

          if (values!=null && values.isNotEmpty){
            int value=values[0];
            if (value==1){
              //加粗
              command="${String.fromCharCode(27)}${String.fromCharCode(33)}${String.fromCharCode(8)}";
            }else{
              //取消加粗
              command="${String.fromCharCode(27)}${String.fromCharCode(33)}${String.fromCharCode(0)}";
            }

          }else{
            //取消加粗
            command="${String.fromCharCode(27)}${String.fromCharCode(33)}${String.fromCharCode(0)}";
          }

          break;
        case PrintCommand_ESC_Text_Align_justification:
        //位置对齐:左0,中1,右2
        //可用于文字或图片对齐
          command="${String.fromCharCode(27)}${String.fromCharCode(97)}${String.fromCharCode(values![0])}";
          break;

        case PrintCommand_ESC_Text_Background_black://黑底白字
          command="${String.fromCharCode(29)}${String.fromCharCode(66)}${String.fromCharCode(1)}";
          break;
        case PrintCommand_ESC_Text_underline:
        /*
                下划线模式
                [格式] ASCII 码 ESC - n
                十六进制码 1B 2D n
                十进制码 27 45 n
                [范围] 0 ≤ n ≤ 2, 48 ≤ n ≤ 50
                [描述] 根据 n 的值选择或取消下划线模式：
                0, 48 取消下划线模式
                1, 49 选择下划 线模式（1 点宽）
                2, 50 选择下划线模式（2 点宽）
                [注释] • 下划线可加在所有字符下（包括右间距），但不包括 HT 设置的空格。
                • 下划线不能作用在顺时针旋转 90°和反色的字符下。
                • 当取消下划线模式时，后面的字符不加下划线，下划线的宽度不改变。默认宽 度是一 点宽。
                • 改变字符大小不影响当前下划线宽度。
                • 下划线选择取消也可以由 ESC !来设置，最后执行的命令有效。
                • 该命令不影响汉字字符的设定。 [默认值] n = 0 [参考] ESC !
                 */
          command="${String.fromCharCode(27)}${String.fromCharCode(45)}${String.fromCharCode(1)}";
          break;
        case PrintCommand_ESC_PageLine:
        //分割线,初始化+居中+分割线+输出命令+初始化(取消居中设备)

        //58mm,默认字体大小,一行16个中文字(数字字母32个),对应gbk编码bytes数组长度为32
          int bytesLengthOfDefaultTextInLine = 32;

          if (values!=null && values.isNotEmpty){
            int pagerSize=values[0];
            bytesLengthOfDefaultTextInLine= XBasePrintCommandUtils.getPaperDefaultTextSizeOneLineStringGBKBytesLength(pagerSize);
          }

          command+="${String.fromCharCode(27)}${String.fromCharCode(64)}";//初始化
          command+="${String.fromCharCode(27)}${String.fromCharCode(97)}${String.fromCharCode(1)}";//设置居中
          for (int i = 0; i<bytesLengthOfDefaultTextInLine; i++){
            command += "-";
          }
          command+=String.fromCharCode(10);
          command+="${String.fromCharCode(27)}${String.fromCharCode(64)}";//初始化(取消居中)

          break;
        case PrintCommand_ESC_CutPaper://切纸(切纸功能需要打印机硬件支持,否则无效)

          if (values!=null && values.isNotEmpty){
            int cutType=values[0];
            if (cutType!=0){
              //全切纸(切断),如果打印机不支持,则是半切纸
              //command=""+(char)27+(char)105; //这条命令有些打印机不支持

              //保证在行首,这条命令只有在行首有效
              //command+=(char)10;
              command="${String.fromCharCode(29)}${String.fromCharCode(86)}${String.fromCharCode(0)}";
              return command;
            }
          }

          //默认半切纸(不完成切断),如果硬件不支持半切纸则为全切纸
          //command=""+(char)27+(char)109;//这条命令有些打印机不支持

          //保证在行首,这条命令只有在行首有效
          //command+=(char)10;
          command="${String.fromCharCode(29)}${String.fromCharCode(86)}${String.fromCharCode(1)}";

          break;
        default:
          break;
      }

      return command;

    }catch(e){
      XLogUtils.printLog('getPrintCommand_ESC报错: $e');
    }

    return command;
  }




}